<?php
namespace WDB\Utils;

use     WDB;

class Strings
{   
    /**
     * Serializes array of strings as a comma separated list (escaping only commas). Produces more human-readable output
     * than generic PHP serialize() method. Does not preserve array keys (they need to be supplied to unserializeCommaSeparatedList)
     *
     * @param string[] array to serialize
     * @return string
     */
    public static function serializeCommaSeparatedList(array $data)
    {
        $result = ''; $first = true;
        foreach ($data as $val)
        {
            if ($first) $first = false;
            else $result .= ',';
            
            $result .= str_replace(array(',', '!'), array('!,', '!!'), $val);
        }
        return $result;
    }
    
    /**
     * Unserializes comma separated list produced by serializeCommaSeparatedList()
     *
     * @param type $data
     * @param array $keys
     * @return type 
     * @see serializeCommaSeparatedList
     * 
     * @throws Exception\WDBException
     */
    public static function unserializeCommaSeparatedList($data, array $keys)
    {
        $result = array();
        reset($keys);
        do
        {
            if (preg_match('~^(?P<head>.*?(?<!\\!)(!!)*),(?P<tail>.*)$~', $data, $matches))
            {
                $data = $matches['tail'];
                $val = $matches['head'];
            }
            else
            {
                $val = $data;
                $data = false;
            }
            $key = each($keys);
            if ($key === false) throw new WDB\Exception\WDBException("number of keys and unserialized values mismatch");
            
            $result[$key[1]] = str_replace(array('!,', '!!'), array(',','!'), $val);
        }
        while ($data !== false);
        if (each($keys)) throw new WDB\Exception\WDBException("number of keys and unserialized values mismatch");
        return $result;
    }
    
    /**
     * Executes preg_match and returns a matched subpattern chosen by these rules:
     * - a named subpattern "read" if present, or
     * - first subpattern if present, or
     * - whole matched string if no subpattern is present
     * 
     * @param string PCRE compatible regular expression
     * @param string string to be matched
     * @return string matched content
     */
    public static function pregRead($regex, $string)
    {
        if (!preg_match($regex, $string, $m)) return FALSE;
        if (isset($m['read'])) return $regex['read'];
        if (isset($m[1])) return $m[1];
        return $m[0];
    }
    
    /**
     * Escapes a string to be a valid PHP variable name. Useful for PHP GET/POST
     * requests where the key must be a valid PHP variable name.
     * 
     * @param string
     * @return string
     */
    public static function escapeVarName($str)
    {
        if ($str == 'this') //PHP only reserved variable name
        {
            return '_74his';
        }
        if ($str == '')
        {
            return '_';
        }
        return preg_replace_callback("~[^a-z\x7f-\xff]~i", function($match) { return '_'.str_pad(dechex(ord($match[0])), 2, '0', STR_PAD_LEFT); }, $str{0})
              .preg_replace_callback("~[^a-z0-9\x7f-\xff]~i", function($match) { return '_'.str_pad(dechex(ord($match[0])), 2, '0', STR_PAD_LEFT); }, substr($str, 1));
    }
    
    /**
     * Unescapes a string encoded by escapeVarName method.
     * 
     * @param string
     * @return string
     */
    public static function unescapeVarName($str)
    {
        return self::unescapeWES($str);
    }
    
    /**
     * Escapes all special characters 
     * 
     * @param string
     * @return string
     */
    public static function escapeWes($str, $regexChars = '~[^a-z0-9]~i')
    {
        return preg_replace_callback($regexChars, function($match) { return '_'.str_pad(dechex(ord($match[0])), 2, '0', STR_PAD_LEFT); }, $str);
    }
    
    
    /**
     * Unescapes a string escaped by any WDB escaping system method.
     * WDB escaping system replaces unwanted special characters(bytes) with '_xx' sequence,
     * where xx is a hexadecimal number from the original byte.
     *
     * @param string
     * @return string
     */
    public static function unescapeWes($str)
    {
        if ($str == '_')
        {
            return '';
        }
        return preg_replace_callback("~_([a-f0-9]{2})~i", function($match) { return chr(hexdec($match[1])); }, $str);
    }
}
